package com.vue.admin.service.common.mybatis.plugins;

import com.vue.admin.service.common.context.EnvironmentThreadContext;
import com.vue.admin.service.common.jwt.ShiroUtil;
import com.vue.admin.service.common.util.CommonUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.scripting.xmltags.DynamicContext;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Properties;

/**
 * @program: vue-admin-service
 * @description: 数据维度拦截器
 * @author: zhangyangyang
 * @create: 2020-04-03 13:49
 **/
@Intercepts({@Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class BaseDataPrivilegeInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //获取MappedStatement
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        //获取sqlsource
        SqlSource sqlSource = ms.getSqlSource();
        Object parameter = invocation.getArgs()[1];
        //判断是否为动态sql
        if (sqlSource instanceof DynamicSqlSource) {
            //通过反射获取sqlNode
            Field field = sqlSource.getClass().getDeclaredField("rootSqlNode");
            field.setAccessible(true);
            //通过反射获取sqlNode
            SqlNode sqlNode = (SqlNode) field.get(sqlSource);
            //对sqlNode进行动态代理
            SqlNode proxyNode = (SqlNode) Proxy.newProxyInstance(sqlNode.getClass().getClassLoader(), new Class[]{SqlNode.class}, new SqlNodeInvocationHandler(sqlNode, ms, parameter));
            field.set(sqlSource, proxyNode);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

    private class SqlNodeInvocationHandler implements InvocationHandler {

        private SqlNode target;
        private MappedStatement ms;
        private Object parameter;

        private SqlNodeInvocationHandler(SqlNode target, MappedStatement ms, Object parameter) {
            this.target = target;
            this.ms = ms;
            this.parameter = parameter;
        }


        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            DynamicContext context = (DynamicContext) args[0];
            //context.bind("DP","1!=1");
            //客户增加维度限制
            context.bind("dp_language", EnvironmentThreadContext.get(EnvironmentThreadContext.LANGUAGE_CONTEXT));
            if (!CommonUtils.isNullOrEmpty(ShiroUtil.getUserEntity())) {
                context.bind("dp_userId", ShiroUtil.getUserEntity().getId());
                context.bind("dp_userName", ShiroUtil.getUserEntity().getName());
//                if(ShiroUtils.getUserEntity().getUserType()==1){
//                    context.bind("dp_clientId",ShiroUtils.getUserEntity().getClientId());
//                }
            }
            return method.invoke(target, args);
        }
    }
}
